package mt.omid.dimomuth;


import java.util.concurrent.ScheduledFuture;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.security.auth.login.ConfigFile;

import mt.omid.dimomuth.bus.DataBus;
import mt.omid.dimomuth.bus.DataBusException;
import mt.omid.dimomuth.internal.ConfigManager;
import mt.omid.dimomuth.internal.ConfigurationException;
import mt.omid.dimomuth.internal.SFAException;
import mt.omid.dimomuth.mgm.ManagementIf;
import mt.omid.dimomuth.mgm.MgmHandler;
import mt.omid.dimomuth.mgm.MgmRpcHandler;
import mt.omid.dimomuth.mgm.XmlRpcIf;


/**
 * Represents a singleton.
 */

/**
 * The class which wants to be a module and be launched by kernel have to
 * extends this class (Module) For Mgm submodule they should pass a MgmHandler
 * to start method of it and start Mgm. For Config submodule kernel which
 * initialize all instances pass proper config file address to initialize method
 * 
 * If module intended to expose management through XmlRpc (added recent) it
 * should implements its handler as MgmRpcHandler not old MgmHandler that act as
 * telnet server.
 */

public abstract class Module implements Runnable
{

    public final static int MOD_STAT_LOADED   = 0;
    public final static int MOD_STAT_INITED   = 1;
    public final static int MOD_STAT_PAUSED   = 2;
    public final static int MOD_STAT_STARTED  = 3;
    public final static int MOD_STAT_SHUTDOWN = 4;

    private int	     state	     = MOD_STAT_LOADED;
    private int	     id;
    protected int	   instId;
    // License Attributes
    private float	   feature;
    private float	   capacity;

    // Logging Attributes
    protected static String logID;
    protected static Logger logger;
    protected Level	 lvl	       = Level.FINEST;

    ScheduledFuture	 schFut;

    // Interface to other modules
    /**
     * @link aggregation
     */

    protected ConfigManager cm;

    /**
     * @link aggregation Configuration manager will be available after calling
     *       initialization method, after constructor call.
     */

    protected DataBus       dBus;

    /**
     * @link aggregation
     */

    protected ManagementIf  mgrIf;


    /***
     * Every subclass have to have a default constructor and call super with
     * logger id something like .class.getSimpleName().
     * 
     * @param logID
     *            used for Logger
     * @param confFile
     *            The file name of module configuration file
     * @throws SFAException
     */
    protected Module( int id, String logID, String confFile )
    {
	// prevent creation
	Module.logID = logID;
	logger = Logger.getLogger( Module.logID );
	setLoggingLevel( lvl );
	state = MOD_STAT_LOADED;
	this.id = id;
	try
	{
	    DataBus.register( this.id );
	}
	catch( DataBusException e )
	{
	    logger.severe( "Fault in registering module in common data bus: " + e.getMessage() );
	}

	checkLicense();

	/*
	 * Change 1 : pass config file by default constructor instead of krn.cfg
	 */
	// start configuration manager
	try
	{
	    cm = new ConfigManager( confFile );
	}
	catch( ConfigurationException e )
	{
	    logger.log( Level.SEVERE, "Config File Load Failed", e );
	}

	this.instId = Kernel.getInstanceId();
    }

    public void setLoggingLevel( Level lvl )
    {
	this.lvl = lvl;
	logger.setLevel( this.lvl );
	setAllHandlersLevel( this.lvl );
    }

    public void setAllHandlersLevel( Level lvl )
    {
	Handler hndl[] = Logger.getLogger( "" ).getHandlers();

	for( int i = 0; i < hndl.length; i++ )
	{
	    hndl[ i ].setLevel( this.lvl );
	}
    }

    public final int getState()
    {
	return this.state;
    }

    /**********************
     * Final methods which will be called by kernel and they call instance
     * abstract implemented mapped method. Map Module (Final) Instance
     * (Abstract) initialize() init() run() start() shutdown() stop()
     */

    /***
     * This method is called by {@link Kernel} to get module config file address
     * and initialize it.
     * 
     * Note: Configuration Manager will be available after constructor calling.
     * It can be use in init() method.
     * 
     * @param confFile
     * @return
     * @throws SFAException
     */
    public final int initialize() throws SFAException
    {
	// start management interface
	// Check if module implements new XmlRpc handler so use it

	MgmHandler hndl = getMgmHandler();

	if( hndl instanceof MgmRpcHandler )
	{
	    mgrIf = new XmlRpcIf( cm.getIntProperty( "mod.mgmport", 9899 ), logger );
	}
	else
	// MgmHandler instance
	{
	    mgrIf = new ManagementIf( cm.getIntProperty( "mod.mgmport", 9899 ), logger );
	}
	mgrIf.start( hndl );

	// call module specific init
	init();

	// start data bus

	state = MOD_STAT_INITED;
	return 0;
    }

    public final void run()
    {
	state = MOD_STAT_STARTED;
	start();
	state = MOD_STAT_SHUTDOWN;
    }

    public final boolean shutdown()
    {
	state = MOD_STAT_SHUTDOWN;
	stop();
	return schFut.cancel( true );
    }

    public final void forceShutdown()
    {
	mgrIf.shutdown();
    }

    public void setExecutor( ScheduledFuture fut )
    {
	this.schFut = fut;
    }

    /**
     * Is used in instantiation to check for licensing
     * 
     * @return
     */
    public int checkLicense()
    {
	return 0;
    }

    /*******************************
     * Abstract method which should be implemented by module subclasses and
     * called by Module final proper methods.
     * 
     * @return
     */

    protected abstract MgmHandler getMgmHandler();

    protected abstract int init();

    protected abstract int start();

    protected abstract int pause();

    protected abstract int stop();

}
